Skip to main content

File operations

This chapter describes how the C language manipulates files.

File pointers

The C language provides a FILE data structure that records the information needed to manipulate a file. This structure is defined in the header file stdio.h and all file manipulation functions pass through this data structure to obtain file information.

Before starting to manipulate a file, a FILE pointer to the file is defined, which is equivalent to getting an area of memory to hold the file information.

FILE* fp;

The above example defines a FILE pointer fp.

The following is a complete example of reading a file.

#include <stdio.h>

int main(void) {
FILE* fp;
char c;

fp = fopen("hello.txt", "r");
if (fp == NULL) {
return -1;
}

c = fgetc(fp);
printf("%c\n", c);

fclose(fp);

return 0;
}

In the above example, after the new file pointer fp is created, the following three file manipulation functions are used in turn, in three steps. Other file operations follow roughly the same steps.

The first step is to use fopen() to open the specified file, returning a File pointer. If there is an error, NULL is returned.

This is equivalent to associating information about the specified file with the new file pointer fp, which records information inside the FILE structure: the current read/write position inside the file, a record of read/write errors, an end-of-file indicator, a pointer to the start of the buffer, a file identifier, a counter (counting the number of bytes copied into the buffer) and so on. Subsequent operations can then use this pointer (rather than the file name) to process the specified file.

At the same time, it creates a buffer for the file. Because of the buffer, it can also be said that the fopen() function "opens a stream" and subsequent read and write files are in stream mode.

The second step is to use the read and write functions to read data from or write data to the file. The above example uses the fgetc() function to read a character from an open file.

Once fgetc() is called, a block of data from the file is first copied to a buffer. The buffer size varies from computer to computer, and is usually 512 bytes or a multiple of that, such as 4096 or 16384. As the hard disk size of a computer gets larger, the buffer gets larger.

fgetc() reads data from the buffer and points the internal read/write position indicator of the file pointer to the next character of the character being read. All file read functions use the same buffer, and any subsequent calls to a read function will start reading from the location pointed to by the indicator, i.e. the location where the previous read function stopped.

When the read function finds that it has read all the characters in the buffer, it requests that the next buffer-sized block of data be copied from the file into the buffer. In this way, the read function reads all the contents of the file up to the end of the file. However, the above example is reading only one character from the buffer. When the function has read the last character of the file inside the buffer, it sets the end-of-file indicator inside the FILE structure to true. The next time the read function is called, it returns the constant EOF, which is an integer value representing the end of the file, usually -1.

The third step, fclose(), closes the file and clears the buffer.

The above is the process of reading the file, the file is written in a similar way, the data is first written to the buffer and when the buffer is filled, the data in the buffer is transferred to the file.

fopen()

The fopen() function is used to open a file. The first step in all file operations is to open the specified file using fopen(). The prototype of this function is defined in the header file stdio.h.

FILE* fopen(char* filename, char* mode);

It accepts two arguments. The first argument is the filename (which can contain the path) and the second argument is a mode string specifying the operation to be performed on the file, e.g. in the example below, `r'' means open the file in read mode.

fp = fopen("in.dat", "r");

After successfully opening the file, fopen() returns a FILE pointer that other functions can use to manipulate the file. If the file cannot be opened (e.g. the file does not exist or does not have permissions), the null pointer NULL will be returned, so after executing fopen(), it is a good idea to determine if the file was opened successfully.

fp = fopen("hello.txt", "r");

if (fp == NULL) {
printf("Can't open file!\n");
exit(EXIT_FAILURE);
}

In the above example, if fopen() returns a null pointer, the program will report an error.

The mode strings for fopen() are as follows.

  • r: read mode, used to read data only. If the file does not exist, a NULL pointer is returned.
  • w: write mode, used only to write data. If the file exists, the file length is truncated to 0 and then written; if the file does not exist, the file is created.
  • a: write mode, used only to append data to the end of the file. If the file does not exist, the file is created.
  • r+: read and write mode. If the file exists, the pointer points to the beginning of the file and data can be appended to the head of the file. If the file does not exist, the NULL pointer is returned.
  • w+: read and write mode. If the file exists, the file length is truncated to 0 and data is then written. This mode does not actually read the data, but instead erases it. If the file does not exist, it is created.
  • a+: Read and write mode. If the file exists, the pointer points to the end of the file and can be added to the end of an existing file. If the file does not exist, the file is created.

As mentioned in the previous subsection, the fopen() function creates a buffer for the opened file. In read mode, a read buffer is created; in write mode, a write buffer is created; in read and write mode, two buffers are created at the same time.C reads and writes data to the file in the form of a stream through the buffer.

The data is stored in binary form inside the file. However, when reading, there are different ways of interpreting it: reading it in its original binary form is called a 'binary stream'; converting binary data to text and interpreting it as text is called a 'text stream'. The same applies to writing operations, which are divided into writing in binary and writing in text, the latter with an additional text-to-binary step.

The fopen() mode string is read and written as a text stream by default. If you add the b suffix (for binary), it will read and write as a "binary stream". For example, rb is the read binary mode and wb is the write binary mode.

The mode string also has an x suffix, which indicates exclusive mode. If the file already exists, then opening the file fails; if the file does not exist, then a new file is created and opened without allowing other programs or threads to access the current file. For example, wx indicates that writing to a file in exclusive mode will fail to open the file if it already exists.

Standard streams

The Linux system provides three already opened files by default, which have the following file pointers.

  • stdin (standard input): the default source is the keyboard and the file pointer is numbered 0.
  • stdout (standard output): the default destination is the monitor and the file pointer is numbered 1.
  • stderr (standard error): the default destination is the monitor and the file pointer is numbered 2.

A file on a Linux system is not necessarily a data file, but can also be a device file, i.e. the file represents a device that can be read or written to. The file pointer stdin by default treats the keyboard as a file, and reading this file gives access to the user's keyboard input. Similarly, stdout and stderr treat the display as a file by default, and write the results of the program to this file so that the user can see the results of the run. The difference is that stdout writes the normal results of the program and stderr writes the error messages of the program.

These three input and output channels, which are provided by Linux by default, are therefore called standard input (stdin), standard output (stdout) and standard error (stderr) respectively. They are collectively referred to as "standard streams" because they are implemented in the same way, as file streams.

Linux allows changing the file to which these three file pointers (file streams) refer, which is called redirection.

If the standard input is not bound to the keyboard, but to another file, you can put a minus sign < in front of the file name, followed by the program name. This is called ``input redirection''.

demo < in.dat

In the above example, stdin in the demo program code will point to the file in.dat, i.e. it will fetch data from in.dat.

If the standard output is bound to another file than the monitor, you can prefix the file name with a greater than sign >, followed by the program name. This is called ``output redirection''.

demo > out.dat

In the above example, stdout in the demo program code will point to the file out.dat, i.e. write data to out.dat.

The output redirection > will first erase all the original contents of out.dat and then write it. If you want the written information to be appended to the end of out.dat, you can use the >> symbol.

demo >> out.dat

In the above example, stdout, inside the demo program code, will write data to the file out.dat. Unlike >, the write starts at the end of the file out.dat.

The standard error redirection symbol is 2>. Where 2 represents the number of the file pointer, i.e. 2> means that the write to file pointer number 2 is redirected to err.txt. file pointer number 2 is the standard error stderr.

demo > out.dat 2> err.txt

In the above example, stderr, inside the demo program code, writes an error message to the file err.txt. And stdout writes to the file out.dat.

Input redirection and output redirection can also be combined in a single command.

$ demo < in.dat > out.dat

// or
$ demo > out.dat < in.dat

There is another case of redirection, where the standard output of one program, stdout, is pointed to the standard input of another program, stdin, where the | symbol is used.

random | sum

In the above example, a write to stdout in the random program code is read from stdin in the sum program code.

fclose()

fclose() is used to close a file that has been opened with fopen(). Its prototype is defined in stdin.h.

int fclose(FILE* stream);

It accepts a file pointer fp as an argument. The fclose()` function returns the integer 0`` if the file is successfully closed, or a special value EOF if the operation fails (e.g. the disk is full, or an I/O error occurs) (see the next subsection for details).

if (fclose(fp) ! = 0)
printf("Something wrong.");

Any file that is no longer in use should be closed using fclose(), otherwise it will not free up resources. Generally, the system has a limit on the number of files that can be open at the same time, and closing files in a timely manner can avoid exceeding this limit.

EOF

The C language file manipulation functions are designed to return a special value if they encounter the end of a file. When the program receives this special value, it knows that it has reached the end of the file.

The header file stdio.h defines a macro EOF (short for end of file) for this special value, which is usually -1. This is because a binary value read from a file, whether interpreted as an unsigned number or as ASCII code, cannot be negative, so it is quite safe to return -1 without conflicting with the data in the file itself.

Note that unlike the end of a string which actually stores the value \0, EOF is not stored at the end of the file, the value does not exist in the file, it is entirely up to the file manipulation function to find that it has reached the end of the file and return the value.

freopen()

freopen() is used to open a new file and associate it directly with a pointer to some already opened file. This allows the file pointer to be reused. Its prototype is defined in the header file stdio.h.

FILE* freopen(char* filename, char* mode, FILE stream);

It is compared to `fopen() in that it has an extra third argument, indicating the file pointer to be reused. The other two parameters are the same, the filename and the open mode, respectively.

freopen("output.txt", "w", stdout);
printf("hello");

The above example associates the file output.txt with stdout and anything written to stdout thereafter will be written to output.txt. Since printf() is output to stdout by default, after running the above code, the file output.txt will be written to hello.

The return value of freopen() is its third argument (a pointer to the file). If the open fails (e.g. the file does not exist), the null pointer NULL will be returned.

freopen() will automatically close a file that was already open, or freopen() is equivalent to fopen() if the file pointer does not point to an already open file.

Here is an example of freopen() associated with scanf().

int i, i2;

scanf("%d", &i);

freopen("someints.txt", "r", stdin);
scanf("%d", &i2);

In the above example, scanf() is called twice, the first call is to read from the keyboard, then freopen() is used to associate the stdin pointer to a file, and the second call will read from that file.

Some systems allow the use of freopen() to change the open mode of a file. In this case, the first argument to freopen() should be NULL.

freopen(NULL, "wb", stdout);

The above example changes the open mode of stdout from w to wb.

fgetc(), getc()

fgetc() and getc() are used to read a character from a file. Their usage is similar to getchar(), except that getchar() is only used to read from stdin, whereas these two functions read from an arbitrary specified file. Their prototypes are defined in the header file stdio.h.

int fgetc(FILE *stream)
int getc(FILE *stream);

The usage of fgetc() and getc() is the same, both have only one argument, the file pointer. The difference between the two is that getc() is generally implemented with macros, while fgetc() is a function implementation, so the former may perform a little better. Note that although both functions return a character, their return value type is not char but int, this is because they return EOF in the case of a failed read, and this value is typically -1.

#include <stdio.h>

int main(void) {
FILE *fp;
fp = fopen("hello.txt", "r");

int c;
while ((c = getc(fp)) ! = EOF)
printf("%c", c);

fclose(fp);
}

In the above example, getc() reads each character of the file in turn, putting it into the variable c until it reaches the end of the file, returning EOF and terminating the loop. The variable c is of type int, not char, because it may be equal to a negative value, so it is better to set it to int.

fputc(), putc()

fputc() and putc() are used to write a character to a file. Their usage is similar to putchar(), except that putchar() writes to stdout, whereas these two functions write to a file. Their prototypes are defined in the header file stdio.h.

int fputc(int char, FILE *stream);
int putc(int char, FILE *stream);

The usage of fputc() and putc() is the same, both accept two arguments, the first being the character to be written and the second being a pointer to the file. The difference between them is that putc() is usually implemented using macros, whereas fputc() is only implemented as a function, so in theory putc() will perform a little better.

On a successful write, they return the character written; on a failed write, they return EOF.

fprintf()

fprintf() is used to write a formatted string to a file, and is used in a similar way to printf(). The difference is that printf() always writes to stdout, whereas fprintf() writes to the specified file, and its first argument must be a file pointer. Its prototype is defined in the header file stdio.h.

int fprintf(FILE* stream, const char* format, ...)

fprintf()` can be used instead of printf()`.

printf("Hello, world!\n");
fprintf(stdout, ``Hello, world!\n``);

In the above example, specifying fprintf() to write to stdout results in the equivalent of calling printf().

fprintf(fp, "Sum: %d\n", sum);

The above example is writing a string of the specified format to the file pointer fp.

The following is an example of outputting an error message to stderr.

fprintf(stderr, ``Something number.\n'');

fscanf()

fscanf()` is used to read from a file according to a given pattern, and is used in a similar way to scanf(). The difference is that scanf()always reads fromstdin, whereas fscanf()reads from a file. Its prototype is defined in the header filestdio.h`, and the first argument must be a file pointer.

int fscanf(FILE* stream, const char* format, ...) ;

Here is an example.

fscanf(fp, "%d%d", &i, &j);

In the above example, fscanf() reads two integers from inside the file fp, into the variables i and j.

The use of fscanf() presupposes knowledge of the structure of the file, and its rules for placeholder parsing are identical to those of scanf(). Since fscanf() can be read continuously until it reaches the end of the file, or until an error occurs (read failure, match failure), it will stop reading, so fscanf() is usually placed inside a loop.

while(fscanf(fp, "%s", words) == 1)
puts(words);

In the example above, fscanf() reads each word of the file in turn, printing them one at a time on a line until the end of the file.

The return value of fscanf() is the number of variables that were successfully assigned; if the assignment fails it returns EOF.

fgets()

fgets() is used to read a string of a specified length from a file, the first character of its name is f, which stands for file. Its prototype is defined in the header file stdio.h.

char* fgets(char* str, int STRLEN, File* fp);

Its first argument, str, is a string pointer to the contents of the read. The second argument, STRLEN, specifies the length of the read, and the third argument is a FILE pointer to the file to be read.

After fgets() reads STRLEN - 1 character, or if it encounters a line break and the end of a file, it stops reading and then adds a null character \0 to the end of what has been read, making it a string. Note that fgets() will store the newline character (\n) into the string.

If the third argument to fgets is stdin, you can read standard input, equivalent to scanf().

fgets(str, sizeof(str), stdin);

On a successful read, fgets() returns its first argument, a pointer to the string, otherwise it returns the null pointer NULL.

`fgets() can be used to read every line of a file, here is an example of reading all lines of a file.

#include <stdio.h>

int main(void) {
FILE* fp;
char s[1024]; // array must be large enough to drop a line
int linecount = 0;

fp = fopen("hello.txt", "r");

while (fgets(s, sizeof s, fp) ! = NULL)
printf("%d: %s", ++linecount, s);

fclose(fp);
}

In the above example, for each line read, the line number and the contents of that line are output.

The following example reads the user's input in a loop.

char words[10];

puts("Enter strings (q to quit):");

while (fgets(words, 10, stdin) ! = NULL) {
if (words[0] == 'q' && words[1] == '\n')
break;

puts(words);
}

puts("Done.");

In the above example, fgets() will read the string multiple times if the user enters more than 9 characters. It won't exit the loop until it encounters q + the enter key.

fputs()

The fputs() function is used to write a string to a file, and differs from the puts() function only in that it does not add a newline to the end of the string. This is because fgets() preserves the newlines, so fputs() doesn't add them. The fputs() function is usually used in conjunction with fgets().

Its prototype is defined in stdio.h.

int fputs(const char* str, FILE* stream);

It accepts two arguments, the first being a pointer to a string and the second being a pointer to the file to be written to. If the second argument is stdout (standard output), it is outputting the contents to the computer screen, equivalent to printf().

char words[14];

puts("Enter a string, please.");
fgets(words, 14, stdin);

puts("This is your string:");
fputs(words, stdout);

In the above example, user input is first read from stdin using fgets(), and then output to stdout using fputs().

On a successful write, fputs() returns a non-negative integer, otherwise it returns EOF.

fwrite()

fwrite() is used to write large chunks of data at once. Its main use is to write arrays of data to a file in one go, and is suitable for writing binary data. Its prototype is defined in stdio.h.

size_t fwrite(
const void* ptr,
size_t size,
size_t nmemb,
FILE* fp
);

It accepts four arguments.

  • ptr: a pointer to the array.
  • size: size of each array member, in bytes.
  • nmemb: the number of array members.
  • fp: a pointer to the file to be written to.

Note that the first argument type of the fwrite() prototype is void*, which is an untyped pointer, and the compiler automatically converts the argument pointer to the void* type. It is because fwrite() does not know the types of the array members that it needs to know the size (second argument) and number of members (third argument) of each member.

The return value of the fwrite() function is the number of array members successfully written (note that it is not the number of bytes). Normally, this return value is the third argument, nmemb, but if there is a write error and only some of the members are written, the return value will be smaller than nmemb.

To write the entire array arr to a file, you can write it in the following way.

fwrite(
arr,
sizeof(arr[0]),
sizeof(arr) / sizeof(arr[0]),
fp
);

In the above example, sizeof(a[0]) is the number of bytes occupied by each array member, and sizeof(a) / sizeof(a[0]) is the number of members of the entire array.

The following example writes a string of size 256 bytes to a file.

char buffer[256];

fwrite(buffer, 1, 256, fp);

In the above example, each member of the array buffer is 1 byte and there are 256 members in total. Since fwrite() is a continuous memory copy, writing fwrite(buffer, 256, 1, fp) will also do the trick.

There is no requirement for fwrite() to write to the whole array; it is fine to write only part of the array.

Any type of data can be thought of as an array of 1 byte of data, or as an array of members, so fwrite() can actually write any type of data, not just an array. For example, fwrite() can write a Struct structure to a file for safekeeping.

fwrite(&s, sizeof(s), 1, fp);

In the above example, s is a pointer to a Struct structure, which can be thought of as an array of members. Note that if the attribute of s contains a pointer, care needs to be taken when storing it, as it may not make sense to save the pointer and there is no guarantee that the data pointed to by the pointer will still exist when it is restored.

fwrite(), and fread() to be described later, are better suited to reading and writing binary data, as they do not decode the data written. Binary data may contain the null character \0, which is a string ending token in C, so reading and writing binary files is not suitable for using text reading and writing functions (such as fprintf(), etc.).

Here is an example of writing to a binary file.

#include <stdio.h>

int main(void) {
FILE* fp;
unsigned char bytes[] = {5, 37, 0, 88, 255, 12};

fp = fopen("output.bin", "wb");
fwrite(bytes, sizeof(char), sizeof(bytes), fp);
fclose(fp);
return 0;
}

In the above example, when writing to a binary file, fopen() is to be opened using the wb mode, indicating a binary write. fwrite() can interpret the data as a single byte array, so its second argument is sizeof(char) and its third argument is the total number of bytes in the array sizeof(bytes).

The file output.bin written in the above example, when opened using a hex editor, would look like the following.

05 25 00 58 ff 0c

fwrite() can also write data to a file in succession.

struct clientData myClient = {1, 'foo bar'};

for (int i = 1; i <= 100; i++) {
fwrite(&myClient, sizeof(struct clientData), 1, cfPtr);
}

In the above example, fwrite() writes 100 consecutive pieces of data to the file.

fread()

The fread() function is used to read larger chunks of data from a file at once. Its main purpose is to read the contents of a file into an array, suitable for reading binary data. Its prototype is defined in the header file stdio.h.

size_t fread(
void* ptr,
size_t size,
size_t nmemb,
FILE* fp
);

It accepts four arguments, identical to fwrite().

  • ptr: the address of the array.
  • size: the size of each array member in bytes.
  • nmemb: the number of members of the array.
  • fp: the file pointer.

To read the contents of a file into the array arr, you can write it in the following way.

fread(
arr,
sizeof(arr[0]),
sizeof(arr) / sizeof(arr[0]),
fp
);

In the above example, the product of the length of the array (the second argument) and the size of each member (the third argument) is the size of the memory space occupied by the array. fread() will read the contents of the same size from the file (fourth argument) and then point ptr (first argument) to the memory address of those contents.

The following example reads the contents of the file into a 10 member double precision floating point array.

double earnings[10];
fread(earnings, sizeof(double), 10, fp);

In the above example, the size of each array member is sizeof(double), and one with 10 members will read sizeof(double) * 10 size from the file fp.

The return value of the fread() function is the number of members of the array that were successfully read. Normally, this return value is the third argument, nmemb, but if there is a read error or the end of the file is read, the return value will be smaller than nmemb. So it is important to check the return value of fread().

fread() and fwrite() can be used in conjunction. Use fwrite() to save the data to a file before the program terminates, and then use fread() to restore the data into memory the next time it is run.

The following is an example of reading the binary file output.bin generated in the previous section.

#include <stdio.h>

int main(void) {
FILE* fp;
unsigned char c;

fp = fopen("output.bin", "rb");
while (fread(&c, sizeof(char), 1, fp) > 0)
printf("%d\n", c);
return 0;
}

After running, the following result is obtained.

5
37
0
88
255
12

feof()

The feof() function determines if the internal pointer to the file points to the end of the file. Its prototype is defined in the header file stdio.h.

int feof(FILE *fp);

feof() accepts a file pointer as an argument. If the end of the file has been reached, a non-zero value is returned (for true), otherwise 0 is returned (for false).

A file read function such as fgetc() that returns EOF has two possibilities, either it has reached the end of the file or there was a read error. feof() can be used to determine which is the case.

Here is an example of reading the whole file in a loop by using feof() to determine if the end of the file has been reached.

int num;
char name[50];

FILE* cfPtr = fopen("clients.txt", "r");

while (!feof(cfPtr)) {
fscanf(cfPtr, "%d%s\n", &num, name);
printf("%d %s\n", num, name);
}

fclose(cfPtr);

The above example reads the entire file by looping to determine if feof() reads the end of the file.

When feof() is true, the state of this function can be cleared by changing the indicator of the internal read/write location of the file with the fseek(), rewind(), and fsetpos() functions.

fseek()

Each file pointer has an internal indicator (internal pointer) that records the read/write position (file position) of the currently open file, i.e. where to start the next read/write from. The file manipulation functions (such as getc(), fgets(), fscanf() and fread()) all read and write files in order from the position specified by this indicator.

If you wish to change this indicator and move it to the specified location in the file, you can use the fseek() function. Its prototype is defined in the header file stdio.h.

int fseek(FILE* stream, long int offset, int whence);

fseek() accepts 3 arguments.

  • stream: a pointer to the file.
  • offset: number of bytes from the reference (third argument). The type is long int and can be positive (move towards the end of the file), negative (move towards the start of the file) or 0 (remain untouched).
  • whence: the position reference, used to determine the starting point of the calculation. Its value is the following three macros (defined in stdio.h): SEEK_SET (at the start of the file), SEEK_CUR (the current position of the internal pointer), SEEK_END (at the end of the file)

See the following examples.

// locate at the start of the file
fseek(fp, 0L, SEEK_SET);

// locate to the end of the file
fseek(fp, 0L, SEEK_END);

// move back 2 bytes from current position
fseek(fp, 2L, SEEK_CUR);

// locate the 10th byte of the file
fseek(fp, 10L, SEEK_SET);

// locate the tenth byte of the file
fseek(fp, -10L, SEEK_END);

In the above example, the second argument to fseek() is of type long, so the move distance must be converted to long by adding the suffix L.

The following example reverses all the bytes of the output file.

for (count = 1L; count <= size; count++) {
fseek(fp, -count, SEEK_END);
ch = getc(fp);
}

Note that fseek() is best used only to manipulate binary files and not to read text files. This is because text files have different encodings for their characters and the exact byte position at a given location is not easy to determine.

Normally, the return value of fseek() is 0. If an error occurs (e.g. moving a distance outside the file), the return value is a non-zero value (e.g. -1).

ftell()

The ftell() function returns the current position of the file's internal indicator. Its prototype is defined in the header file stdio.h.

long int ftell(FILE* stream);

It accepts a file pointer as an argument. The return value is an integer of type long indicating the current position of the internal indicator, i.e. the number of bytes from the start of the file to the current position, 0 indicates the start of the file. If an error occurs, ftell() returns -1L.

ftell() can be used in conjunction with fseek() to first record the position of the internal pointer, and after a series of operations, return the original position with fseek().

long file_pos = ftell(fp);

// After a series of file operations
fseek(fp, file_pos, SEEK_SET);

The following example first locates the indicator at the end of the file, and then gets the number of bytes from the start of the file to the end.

fseek(fp, 0L, SEEK_END);
size = ftell(fp);

rewind()

The rewind() function returns the file's internal indicator to the beginning of the file. Its prototype is defined in stdio.h.

void rewind(file* stream);

It accepts a file pointer as an argument.

rewind(fp)` is basically equivalent to fseek(fp, 0l, seek_set), the only difference being that ``rewind() has no return value and clears the error indicator for the current file.

fgetpos(), fsetpos()

One potential problem with fseek() and ftell() is that they both limit the file size to what can be represented by the long int type. This may seem quite large, but on a 32-bit computer, long int is 4 bytes long and can represent up to 4GB. as storage devices grow rapidly in size, files get larger and often exceed this range. In view of this, C has added two new locator functions to handle large files: fgetpos() and fsetpos().

Their prototypes are both defined in the header file stdio.h.

int fgetpos(FILE* stream, fpos_t* pos);
int fsetpos(FILE* stream, const fpos_t* pos);

The fgetpos() function takes the current position of the file's internal indicator and stores it in the pointer variable pos. The function accepts two arguments, the first being a file pointer and the second storing the variable for the indicator position.

The fsetpos() function will move the position of the file's internal indicator to the address specified by the pointer variable pos. Note that the variable pos must be obtained by calling the fgetpos() method. The two arguments to fsetpos() must be the same as those to fgetpos().

The pointer variable pos, which records the position of the file's internal indicator, is of type fpos_t* (short for file position type, meaning file positioning type). It does not have to be an integer, but may also be a Struct structure.

Here is an example of usage.

fpos_t file_pos;
fgetpos(fp, &file_pos);

// After a series of file operations
fsetpos(fp, &file_pos);

In the above example, fgetpos() is used to get the position of the internal pointer first, and then fsetpos() is used later to restore the position of the pointer.

On successful execution, both fgetpos() and fsetpos() return 0, otherwise a non-zero value is returned.

ferror(), clearerr()

All file manipulation functions will record an error status inside the file pointer if they fail to execute. Subsequent operations simply read the error indicator to know that the previous operation was in error.

The ferror() function is used to return the status of the error indicator. This function can be used to determine if the previous file operation was successful. Its prototype is defined in the header file stdio.h.

int ferror(FILE *stream);

It accepts a file pointer as an argument. ferror() returns a non-zero integer (denoted true) if the previous operation is wrong, otherwise it returns 0.

The clearerr() function is used to reset the error indicator. Its prototype is defined in the header file stdio.h.

void clearerr(FILE* fp);

It accepts a file pointer as an argument and has no return value.

Here is an example.

FILE* fp = fopen("file.txt", "w");
char c = fgetc(fp);

if (ferror(fp)) {
printf("Error occurred while reading file: file.txt \n");
}

clearerr(fp);

In the above example, fgetc() tries to read a file opened in "write mode" and returns EOF if the read fails, then call ferror() to know that there was an error in the previous step. After processing, clearerr() will clear the error status.

If the file manipulation function executes normally, ferror() and feof() will both return zero. If the execution is not normal, it is important to determine what went wrong.

if (fscanf(fp, "%d", &n) ! = 1) {
if (ferror(fp)) {
printf("io error \n");
}
if (feof(fp)) { if (feof(fp))
printf("end of file\n");
}

clearerr(fp);

fclose(fp);
}

In the example above, when the fscanf() function reports an error, determine exactly what is going wrong by checking ferror() and feof(). These two indicators will remain unchanged after changing state, so clear them with clearerr(), which clears both indicators.

remove()

The remove() function is used to remove the specified file. Its prototype is defined in the header file stdio.h.

int remove(const char* filename);

It accepts the filename as an argument. remove()` returns 0`` if the removal was successful, otherwise it returns a non-zero value.

remove("foo.txt");

The above example removes the foo.txt file.

Note that the deletion must be in the closed state of the file. If the file was opened with fopen(), it must be closed with fclose() before it can be deleted.

rename()

The rename() function is used to rename a file and also to move it. Its prototype is defined in the header file stdio.h.

int rename(const char* old_filename, const char* new_filename);

It accepts two arguments, the first being the current filename and the second being the new one. If the rename succeeds, rename()` returns 0``, otherwise it returns a non-zero value.

rename("foo.txt", "bar.txt");

The above example renames foo.txt to bar.txt.

Note that the renamed file cannot have the same name as the existing file. Also, if the file to be renamed is already open, it must be closed first and then renamed; renaming an open file will fail.

Here is an example of moving a file.

rename("/tmp/evidence.txt", "/home/beej/nothing.txt");